package main

import "curses"
import "os"
import "fmt"
import "strings"
import "markers"
import "panes"
import "parser"

var text =
`(1, 2 => x+
`

const KEY_RETURN = 10

func main() {
	startGoCurses();
	defer stopGoCurses();
//
	curses.Init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK);
    curses.Init_pair(2, curses.COLOR_GREEN + 8, curses.COLOR_WHITE + 8)
    curses.Init_pair(3, curses.COLOR_BLUE, curses.COLOR_WHITE + 8)
    curses.Init_pair(7, curses.COLOR_WHITE + 8, curses.COLOR_RED + 8)
//	
	loop()
}

func startGoCurses() {
	curses.Initscr()
	if curses.Stdwin == nil {
		stopGoCurses()
		os.Exit(1)
	}
	
	curses.Raw()
	curses.Noecho()	
	curses.Stdwin.Keypad(true)
    curses.EnableMouse()
	
	if err := curses.Start_color(); err != nil {
		fmt.Printf("%s\n", err.String())
		stopGoCurses()
		os.Exit(1)
	}
    curses.InitColor( 7, 500, 500, 0 )
}

func stopGoCurses() {
	curses.Endwin()
}

func loop() {
    w := curses.Stdwin
    w.Clear()
    lines := make([][]byte, 80)
    for i, _ := range lines { 
        lines[i] = make([]byte, 100) 
        for j := range lines[i] { lines[i][j] = ' ' }
    }
    for i, s := range strings.Split( text, "\n", -1 ) { copy(lines[i], []byte(s) ) }

    for i, line := range lines {
        w.Addstr( 1, i+1, string(line), curses.Color_pair(0) )
    }

    x, y := 1, 1
    waits := 0
    changePending := false
    bigChange := true
    markerz := []markers.Marker{}
    prevMarkers := []markers.Marker{}
    
    cols := *curses.Cols;
    rows := *curses.Rows;
    wp := panes.NewWindowPane( cols, rows )
    xsource, xcomments := panes.SplitVertical( wp )
    source := panes.Frame(xsource)
    comments := panes.Frame(xcomments)
    totalLines, topLine := 60, 1
    for {
        ch := int(lines[y-1][x-1])
        comment := displayMarkers( source, comments, prevMarkers, markerz, x, y, fmt.Sprintf( "current character: '%c'", ch ) )      
        comments.DisplayFrame( "" )  
        source.DisplayFrame( comment )
        if bigChange { displayLines( source, lines ); bigChange = false }
        w.Move( x, y )
		w.Refresh()
		curses.Timeout( 2000 )
		// inp, my, mx := current.Key()
		inp, mx, my := w.Getch(), 0, 0
		if inp == curses.KEY_MOUSE { mx, my, _ = curses.MouseEvent() }
		
		
		if inp == curses.ERR && changePending || inp == 'd' - 'a' + 1 {
	       waits += 1
	       // w.Addstr( 10, 1, "%v", 0, waits )
	       prevMarkers = markerz
	       markerz = parser.GetMarkers( lines )
	       changePending = false	
		}
		if inp == 'q' - 'a' + 1 { break }
	    if inp == curses.KEY_MOUSE { 
	        x, y = mx, my
	    }
		if inp == curses.KEY_LEFT && x > 1 { x = x - 1; }
		if inp == curses.KEY_RIGHT { x = x + 1; }
		if inp == curses.KEY_UP && y > 1 { y = y - 1; }
		if inp == curses.KEY_DOWN { y = y + 1; }
		              
        if inp == KEY_RETURN {
           changePending = true
           bigChange = true
           current := lines[y-1]
           for line := len(lines); y < line;  {
               line -= 1
               lines[line] = lines[line - 1]
           }
           lines[y-1] = bigger( current[0:x-1] )
           lines[y] = bigger( current[x-1:] )
           y += 1
           x = 1
        }
        
        if inp == 'A' { totalLines += 1 
        } else if inp == 'B' && totalLines > 0 { totalLines -= 1 
        } else if inp == 'C' { topLine += 1 
        } else if inp == 'D' && topLine > 1 { topLine -= 1  
		} else if ' ' <= inp && inp < 127 {
		    copy( lines[y-1][x:], lines[y-1][x-1:] ) 
		    lines[y-1][x-1] = byte(inp) 
		    changePending = true 
		    source.PutString( x, y, string(lines[y-1][x-1:]), 0 )
		    x += 1
		}
		if inp == curses.KEY_BACKSPACE && x > 1 {
		  x -= 1
		  copy( lines[y-1][x-1:], lines[y-1][x:] )
		  changePending = true
		  w.Addstr( x, y, string(lines[y-1][x-1:]), 0 ) 
		}
        if inp == curses.KEY_DC && x > 1 { // same as BS except no x -= 1
          copy( lines[y-1][x-1:], lines[y-1][x:] )
          changePending = true
          w.Addstr( x, y, string(lines[y-1][x-1:]), 0 ) 
        }
        source.SetVerticalScrolling( topLine, totalLines )
	}
}

func bigger( s []byte ) []byte {
    result := make([]byte, 100)
    for i := 0; i < len(result); i += 1 { result[i] = ' ' }
    copy(result, s)
    return result;
}

func min( x, y int ) int {
    if x < y { return x } else { return y }
    return 17 // BAH
}

func displayLines( p panes.Pane, lines [][]byte ) {
    _, height := p.Size()
    limit := min( height, len(lines) )
    for y, line := range lines[0:limit] {
        panes.ClearLine( p, y + 1, '*' )
        p.PutString( 1, y + 1, string(line), 0 )
    }
}

func displayMarkers( source, comments *panes.FramedPane, prevMarkers, markerz [] markers.Marker, x, y int, comment string ) string {
    colour := curses.Color_pair(0)
//    
    width, _ := source.Size()
    for i, m := range prevMarkers {
        source.ClearMark( m.Covers.Start.Line )
        source.Highlight( 1, m.Covers.Start.Line, width, panes.Style(colour) )
        panes.ClearLine( comments, i + 1, '!' )
    }
//
    for i, m := range markerz {
        line := m.Covers.Start.Line
        colStart, colEnd := m.Covers.Start.Col, m.Covers.Start.Col + m.Covers.Length - 1
        source.SetMark( line ).Highlight( colStart, line, m.Covers.Length, 7 )
        message := fmt.Sprintf( "%v.%v-%v: %s", line, colStart, colEnd, m.Message )
        comments.PutString( 1, i + 1, message, panes.Style(colour) )
        if y == line && colStart <= x && x <= colEnd { comment = m.Message } 
    }
//
    return comment
}

